package cn.summit.custom.springframework;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import cn.summit.custom.springframework.annotation.Autowired;
import cn.summit.custom.springframework.annotation.Component;
import cn.summit.custom.springframework.annotation.ComponentScan;
import cn.summit.custom.springframework.annotation.Scope;

/**
 * @author summit
 * @since 2022/6/9 17:14
 */
public class SummitApplicationContext {
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    private final Map<String, Object> singletonObjects = new HashMap<>();

    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    public SummitApplicationContext(Class<?> confClazz) {
        scan(confClazz);

        for (Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(entry.getKey());
            if ("singleton".equals(beanDefinition.getScope())) {
                singletonObjects.put(entry.getKey(), createBean(entry.getKey(), entry.getValue()));
            }
        }
    }

    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getType();
        try {
            Object o = clazz.newInstance();

            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    Object bean = getBean(field.getName());
                    field.setAccessible(true);
                    field.set(o, bean);
                }
            }

            if (o instanceof NameWare) {
                ((NameWare) o).setBeanName(beanName);
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                o = beanPostProcessor.postProcessBeforeInitialization(o, beanName);
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                o = beanPostProcessor.postProcessAfterInitialization(o, beanName);
            }
            return o;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 扫描指定包获取class文件信息
     * @param confClazz class
     */
    public void scan(Class<?> confClazz) {
        if (confClazz.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScanAnnotation = confClazz.getAnnotation(ComponentScan.class);
            String path = componentScanAnnotation.value();

            path = path.replace(".", "/");
            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);
            File file = new File(resource.getFile());

            List<File> classFile = new ArrayList<>();
            if (file.isDirectory()) {
                for (File listFile : file.listFiles()) {
                    if (listFile.isDirectory()) {
                        for (File f : listFile.listFiles()) {
                            if (!f.isDirectory()) {
                                classFile.add(f);
                            }
                        }
                    } else {
                        classFile.add(listFile);
                    }
                }
            }
            for (File cFile : classFile) {
                String absolutePath = cFile.getAbsolutePath();
                String className = absolutePath.substring(absolutePath.indexOf("cn"), absolutePath.indexOf(".class"))
                        .replace("\\", ".");

                try {
                    Class<?> clazz = classLoader.loadClass(className);
                    if (clazz.isAnnotationPresent(Component.class)) {

                        // beanPostProcessor
                        if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                            try {
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();

                                beanPostProcessorList.add(beanPostProcessor);
                            } catch (InstantiationException | IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);
                        if (clazz.isAnnotationPresent(Scope.class)) {
                            beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                        } else {
                            beanDefinition.setScope("singleton");
                        }

                        String beanName = clazz.getAnnotation(Component.class).value();
                        if ("".equals(beanName)) {
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

        }
    }

    public Object getBean(String beanName) {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if ("singleton".equals(beanDefinition.getScope())) {
            Object o = singletonObjects.get(beanName);
            if (o == null) {
                o = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, o);
            }
            return o;
        } else {
            return createBean(beanName, beanDefinition);
        }
    }
}
